The terms elements and element nodes are used interchangeably but when people say the DOM is working with an element, it is actually working with a node that represents that element
Document.getElementById(elementId: string): HTMLElement
// by CSS selector
ParentNode.querySelector<Element>(selectors: string): Element
Document.getElementsByClassName(classNames: string): HTMLCollectionOf<Element>
Document.getElementsByTagName(TagName: string): HTMLCollectionOf<Element>
// by CSS selector
ParentNode.querySelectorAll<Element>(selectors: string): NodeListOf<Element>
Note: A NODELIST is not a array but instead is a collection so it has a
lengthproperty and array like ordered index numbers
Note: You can use the index number just like array [index : number] the less prefered way is by using
.item(index: number)method
Live NodeList : when script updates the nodes they are reflected in the collection the methods beginning with getElementBy* return live nodelists also typically faster
Static NodeList : script updates not reflected in the nodes methods beginning with querySelector* return static nodelists
parentNode previousSibling / nextSibling firstChild / last Child
.
Some browsers add text node whenever they come across whitespace between elements
this is why libraries such as Jquery became popular to deal with this complication
| Event | Description |
|---|---|
| UI | Events |
|---|---|
| load | web page has finished loading |
| unload | web page is unloading |
| error | browser encounters a js error or an asset doesn't exist |
| resize | browser window has been resized |
| scroll | user has scrolled up or down the page |
| KeyBoard | Events |
|---|---|
| keydown | user first presses a key repeats while a key is depressed |
| keyup | user releases a key |
| keypress | character is being inserted repeats while a key is depressed |
| Mouse | Events |
|---|---|
| click | user presses and releases a button over the same element |
| dblclick | user presses and release a button twice over the same element |
| mousedown | user presses a mouse button while over an element |
| mouseup | user releases a mouse button while over an element |
| mousemove | user moves the mouse not on a touchscreen |
| mouseover | user moves the mouse over an element not on a touchscreen |
| mouseout | user moves the mouse off an element not on a touchscreen |
preferable technique is to use CSS eg :hover pseudo-class
mousedown/up are separate for drag and drop functionality
| Focus | Events |
|---|---|
| focus / focusin | elements gains a focus |
| blur / focusout | element loses focus |
| Form | Events |
|---|---|
| input | Value in any <input> or <textarea> elemtn has changed or any element with the contenteditable attribute |
| change | value in select box, checkbox, or radio button changes |
| submit | user submits a form using a button or a key |
| reset | user clicks on a form's reset button rarely used these days |
| cut | user cuts content from a form field |
| copy | user coppies content from a form field |
| paste | user pastes a content into a form field |
| select | user selects some text in a form field |
| Mutation | Events |
|---|---|
| DOMSubtreeModified | Change has been made to document |
| DOMNodeInserted | Node has been inserted as a direct child of another node |
| DOMNodeRemoved | Node has been removed from a another node |
| DOMNodeInsertedIntoDocument | Node has been inserted as a descendeant of another node |
| DOMNodeRemovedFromDocument | Node has been removed as a descended of another node |
There are 3 major types of event handlers
HTML Event Handler
This is bad practice and out dated but may still arise in legacy code<a onclick="hide()">
This method is bad becuase it doesn't seperate javascript code from the html
Traditional Event Handler
Unlike with html we attach using only javascript
element.onEvent = functionName
Event Listeners
element.addEventListener(event,functionName [, Boolean])
el.addEventListener('blur', checkUsername, false )
Bassically you get a DOM node and then attach a event listener to that node you have the event and attach one of the functions the third argument is called capture and is ussually set to false
<input type="text" id="username" onblur="checkUsername()">
function fns(){
// function code to run when event is triggered
}
// get element
const el = document.getElementById('id')
// attach handler method
el.onblur = fns // attach function
// attach listener method
el.addListerer('blur', fns, false)
// we can use anonymous functions to pass function arguments
usernameInput.addEventListener('blur', () => {
checkUsername(3)
})
graph BT; a((< a >)) --> li li(< li >) --> ul ul(< ul >) --> body body(< body >) --> html html(< html >) --> Document classDef green stroke:SpringGreen, stroke-width: 1px classDef el fill:green class a,li,ul,body,html,Document green class a el
graph TD; Document --> html(< html >) html --> body(< body >) li --> a((< a >)) ul --> li(< li >) body --> ul(< ul >) classDef red stroke:firebrick, stroke-width: 1px classDef el fill:indianred, color:black class a,li,ul,body,html,Document red class a el
When an event occurs, the event tells you information about the event and the element it happened upon
function checkUsername(e, minLength) {
e.preventdefault() // prevent the default behaviour of going somewhere else
e.stopPropagation() // prevent the bubble event propagation
/* Properties of e */
e.type // type of event being fired
e.target // target of event (the element)
e.cancelable // wherther you can cancel default behaviour of element
}
// this is where we get the event object "e"
el.addEventListener('blur', (e) => checkUsername(e, 5))
graph TB; parent>get <code>< ul ></code> element for shopping list] --> addevent{is<br><code>addEventListener</code><br>supported} addevent -- Yes --> y[use <code>attachEventListener</code>] addevent -- No --> n[use <code>attachEvent</code>] y --> event(<b>Event <code>click</code> on any link in the list</b>) n --> event event --> a subgraph Function:<code>itemDone</code>Removes an item when completed a[<b>Create Variables:</b><br><code>target</code>: element that was clicked]--> b[Get element clicked Call <code>getTarget</code>] b-->e>remove <code>< li ></code> from <code>< ul ></code>] e-->f{is <code>preventDefault</code><br>Supported} f--Y-->preventDefault f--N-->returnValue end
graph TB subgraph Function:<code>getTarget</code>Get element that user clicked f{is there no<br><b>Event Object</b> }--Y-->a[Get target of event] f--N-->b[Get target of event] end
/* Event Scripts (of above diagram) */
const usernameInput = document.getElementById('username')
// usernameInput.onblur = checkUsername // traditional event handler
usernameInput.addEventListener('blur', () => {
return checkUsername(3)
}) // event listener method
function checkUsername(maxlength) {
const elMsg = document.getElementById('feedback')
const elUsername = document.getElementById('username')
// @ts-ignore // HTMLINPUT TYPE BREAKS CODE
const userLength = elUsername.value.length
elMsg.textContent =
userLength < maxlength
? `Username must be ${maxlength} characters or more\nYou need ${
maxlength - userLength
} more`
: ''
}
/** EVENT FLOW **/
// Set up event listeners to call itemDone() on click
const shoppingList = document.getElementById('shoppingList')
addClickEvent(shoppingList)
function addClickEvent(el) {
el.addEventListener ? defaultAddListenerMethod(el) : oldAddListenerMethod(el)
function defaultAddListenerMethod(el) {
el.addEventListener('click', (e) => {
// e.stopPropagation() // note important to prevent bubbling causing link to go elsewhere
itemDone(e)
})
}
/** For Old IE method of calling itemDone */
function oldAddListenerMethod(el) {
el.attachEvent('onclick', (e) => itemDone(e))
}
}
function getTarget(e) {
// For old IE event object
if (!e) e = window.event
// Get the target of event
return e.target || e.srcElement
}
function itemDone(e) {
const target = getTarget(e)
if (target.nodeName.toLowerCase() == 'a') {
const elListItem = target.parentNode
const elList = elListItem.parentNode
elList.removeChild(elListItem)
}
if (target.nodeName.toLowerCase() == 'em') {
const elListItem = target.parentNode.parentNode
const elList = elListItem.parentNode
elList.removeChild(elListItem)
}
/* Prevent the link from taking you elsewhere if preventDefault() works use preventDefault() otherwise Use old IE version similiar for stopping event propagation */
e.stopPropagation ? e.stopPropagation() : (e.cancelBubble = true)
e.preventDefault ? e.preventDefault() : (e.returnValue = false)
}
submit
When a form is submitted, this event fires on the node representing the form element
Ussually used when user is ready to send form data to server
change
Fires when a one of several form elements changes eg
input
Used on <input> or <textarea> elements
The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature
const observer = new MutationObserver(() => {
console.log("There was a DOM change detected");
observer.disconnect();
})
observer.observe(targetNode, { attributes: true, childList: true, subtree: true } as config)
DOMContentLoaded
This event fires when the DOM tree is loaded but images, CSS, Javascript may turn out to still be loading
It can be attached to the window or document object
Note: Since the load event for scripts could be fired after above event this means that the html inserted by script might not have finished yet
window.addEventListener('DOMContentLoaded',
() => textInput.focus() as callback
, false)
beforeunload
This event should only be used to tell the user that form data will not be saved, thus browsers make sure that this cannot be abused for anything else such as telling user to not redirect from page
window.addEventListener('beforeunload', (event) => {
// for firefox this is the only way to activate this event
event.preventDefault()
/* Alternative way to activate event for other browsers */
// const message = 'You have changes that have not been saved... '
// (event || window.event).returnValue = message
// return message
})
There are other events for mobile and some not listed here